#include "Water.h"

Water::Water(const LPDIRECT3DDEVICE9 p_dx_Device, 
			 const WORD width, 
			 const WORD depth) : 
				dampening(0.8f), 
				dimpleCnt(4), 
				width(width),
				depth(depth)
{
	waterBank = Float3D(2, Float2D(width + 1, Float1D(depth + 1)));
	GenerateWaterMesh(p_dx_Device, width, depth);
}

Water::~Water(void)
{
	adjacencyBuffer->Release();
	waterMesh->Release();
	waterBank.clear();
}

void Water::Draw()
{
	waterMesh->DrawSubset(0);
}


void Water::UpdateWater()
{
	waterUpdateRate++;
	if (waterUpdateRate < 4)
	{
		return;
	}
	waterUpdateRate = 0;

	dyna = 0.0f;

	FLOAT damp = 0.9f;
	FLOAT reduc = 0.5f;

	//process water
	for (int x = 1; x <= width - 1; ++x) 
	{
		for (int z = 1; z <= depth - 1; ++z) 
		{
			waterBank[1][x][z] = 
				(
				waterBank[0][x - 1][z] + 
				waterBank[0][x + 1][z] + 
				waterBank[0][x][z + 1] + 
				waterBank[0][x][z - 1]
			) * reduc - waterBank[1][x][z]; 

			waterBank[1][x][z] = waterBank[1][x][z] * damp;

			dyna = dyna + waterBank[1][x][z];
		}
	}

	UpdateWaterMesh();

	Float2D tmp = waterBank[0];
	waterBank[0] = waterBank[1];
	waterBank[1] = tmp;


}

void Water::UpdateWaterMesh()
{
	if (dyna != 0.0)
	{
		vertexInfo::D3DVERTEX *ptr;
		waterMesh->LockVertexBuffer(0, (void**)&ptr);
		//PatchTransform
		UINT k=0;
		for (int x = 0; x <= width; ++x) 
		{
			for (int z = 0; z <= depth; ++z) 
			{
				D3DXVECTOR2 newTexCoords(ptr[k].texCoord.x, ptr[k].texCoord.y);
				newTexCoords.x += (sin(static_cast<FLOAT>(waterBank[1][z][x])) * 0.01f) + waterBank[1][z][x];
				newTexCoords.y += (cos(static_cast<FLOAT>(waterBank[1][z][x])) * 0.01f) + waterBank[1][z][x];

				memcpy(
					&ptr[k],
					&vertexInfo::D3DVERTEX(
					D3DXVECTOR3(ptr[k].position.x, waterBank[1][z][x], ptr[k].position.z),
					D3DXVECTOR3(0, 0, 0),
					newTexCoords,
					D3DXVECTOR3(0.0f, 0.0f, 0.0f)),
					sizeof(vertexInfo::D3DVERTEX));

				k = k + 1;
			}
		}
		waterMesh->UnlockVertexBuffer();

		// Recompute the normals for lighting
		DWORD *buff = (DWORD*)adjacencyBuffer->GetBufferPointer();
		D3DXComputeNormals(waterMesh, buff);
	}
}

void Water::DimpleWater(const FLOAT x, const FLOAT z, const FLOAT force, const FLOAT range)
{
	for (FLOAT xg = x - range * 0.5f; xg <= x + range * 0.5f; ++xg) 
	{
		for (FLOAT zg = z - range * 0.5f; zg <= z + range * 0.5f; ++zg) 
		{
			if (xg > 0.0f && xg < width && zg > 0.0f && zg < depth)
			{
				waterBank[1][xg][zg] = force;
			}
		}
	}
}
// Creates a flat grid mesh
bool Water::GenerateWaterMesh (const LPDIRECT3DDEVICE9 p_dx_Device, const WORD width = 40, const WORD depth = 40)
{
	const FLOAT mix = (width + depth) * 0.5f;

	const FLOAT stx = -0.5f;
	const FLOAT sty = stx;
	const FLOAT stp = 1.0f / mix;
	FLOAT y = sty;

	HRESULT hr;
	const DWORD numSubsets = 0;

	const DWORD faces = width * depth * 2;
	const DWORD verticies = (width + 1) * (depth + 1);

	hr = D3DXCreateMesh(
		faces + 1,
		verticies ,
		D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
		vertexInfo::VERTEX_FORMAT,
		p_dx_Device,
		&waterMesh);
	SHOW_ERROR_MSG(hr, L"Error creating water mesh.");

	// Vertex Buffer
	{
		vertexInfo::D3DVERTEX *vtx = NULL;
		hr = waterMesh->LockVertexBuffer(0, (void**)&vtx);
		if(FAILED(hr)) MessageBox(NULL,DXGetErrorDescriptionW(hr), L"Error creating water vertex buffer.",MB_OK);
		DWORD vtxCnt = 0;
		for (UINT a = 0; a <= depth; ++a)
		{
			FLOAT x = stx;
			const FLOAT v = a/(float)depth;

			for (UINT b = 0; b <= width; ++b)
			{
				FLOAT u = b/(float)width;
				vtx[vtxCnt++] = vertexInfo::D3DVERTEX(
					D3DXVECTOR3(x,		0.0f,	y),
					D3DXVECTOR3(0.0f,	1.0f,	0.0f),
					D3DXVECTOR2(u,		v),
					D3DXVECTOR3(0.0f,	0.0f,	0.0f));

				x = x + stp;
			}
			y = y + stp;
		}
		hr = waterMesh->UnlockVertexBuffer();
		SHOW_ERROR_MSG(hr, L"Error unlocking vertex buffer.");
	}

	// Index Buffer
	{
		WORD *idx = 0;
		hr = waterMesh->LockIndexBuffer(0, (void**)&idx);
		SHOW_ERROR_MSG(hr, L"Error creating index buffer.");
		DWORD idxCnt = 0;
		std::vector<WORD> indexList;
		for (WORD a = 0; a <= depth - 1; ++a)
		{
			for (WORD b = 0; b <= width - 1; ++b)
			{
				const WORD v0 = a * (width + 1) + b;
				const WORD v1 = v0 + 1;
				const WORD v2 = (a + 1) * (width + 1) + b + 1;
				const WORD v3 = v2 - 1;

				idx[idxCnt++] = v0;
				idx[idxCnt++] = v2;
				idx[idxCnt++] = v1;

				idx[idxCnt++] = v0;
				idx[idxCnt++] = v3;
				idx[idxCnt++] = v2;
			}
		}
		hr = waterMesh->UnlockIndexBuffer();
		SHOW_ERROR_MSG(hr, L"Error unlocking index buffer.");
	}

	// Generate Adjacency buffer
	hr = D3DXCreateBuffer(faces * 4 * sizeof(DWORD), &adjacencyBuffer);
	SHOW_ERROR_MSG(hr, L"Error creating adjacencyBuffer.");
	
	DWORD *buff = (DWORD*)adjacencyBuffer->GetBufferPointer();
	hr = waterMesh->GenerateAdjacency(0.0f, buff);
	SHOW_ERROR_MSG(hr, L"Error generating adjacency buffer.");

	return true;
}
